package com.zoe.onelink.core.autoconfigure;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesAnnotationIntrospector;
import com.zoe.onelink.annotation.data.encryptor.IEncryptor;
import com.zoe.onelink.core.encryptor.EncryptorFactory;
import com.zoe.onelink.core.encryptor.EncryptorManager;
import com.zoe.onelink.core.property.OnelinkProperties;
import com.zoe.onelink.core.web.jackson.introspect.OnelinkJacksonAnnotationIntrospector;
import com.zoe.onelink.core.web.jackson.provider.OnelinkSerializerProvider;
import com.zoe.onelink.core.web.jackson.sensitive.strategy.ISensitiveStrategy;
import com.zoe.onelink.core.web.jackson.sensitive.strategy.SensitiveStrategyManager;
import com.zoe.onelink.core.web.jackson.serializer.JacksonDateDeserializer;
import com.zoe.onelink.core.web.jackson.serializer.JacksonEnumSimpleDeserializers;
import com.zoe.onelink.core.web.resolver.ObjectMethodArgumentResolver;
import com.zoe.onelink.core.web.resolver.PageMethodArgumentResolver;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-07-17
 */
@Configuration(proxyBeanMethods = false)
@Import({EncryptorFactory.class})
public class OnelinkJacksonAutoConfiguration {

    public OnelinkJacksonAutoConfiguration(ObjectMapper objectMapper, OnelinkProperties onelinkProperties) {

        // 设置Jackson自定义注解扫面器
        this.setJacksonObjectMapperAnnotationIntrospector(objectMapper, onelinkProperties);
        this.setModule(objectMapper, onelinkProperties);
        OnelinkProperties.Web.Response responseProp = onelinkProperties.getWeb().getResponse();
        // 返回结果字段为null自动填充为空字符串
        if (responseProp.isNullToEmpty()) {
            List<OnelinkProperties.Web.Response.Type> nullToEmptyTypes = responseProp.getNullToEmptyTypes();
            List<Class<?>> typeClasses = nullToEmptyTypes.stream()
                    .map(OnelinkProperties.Web.Response.Type::getValue)
                    .collect(Collectors.toList());
            OnelinkSerializerProvider serializerProvider = new OnelinkSerializerProvider(
                    typeClasses.isEmpty() ? List.of(Object.class) : typeClasses
            );
            objectMapper.setSerializerProvider(serializerProvider);
        }
    }

    @Bean
    public JacksonEnumSimpleDeserializers jacksonEnumSimpleDeserializers() {
        return new JacksonEnumSimpleDeserializers();
    }

    @Bean
    public SensitiveStrategyManager sensitiveStrategyManager(Map<String, ISensitiveStrategy> strategyMap) {
        return new SensitiveStrategyManager(strategyMap);
    }

    @Bean
    public EncryptorManager encryptorManager(Map<String, IEncryptor> encryptorMap) {
        return new EncryptorManager(encryptorMap);
    }


    @Configuration(proxyBeanMethods = true)
    @ConditionalOnProperty(name = "onelink.web.request-arg.enabled", matchIfMissing = false)
    @AllArgsConstructor
    @Slf4j
    static class OnelinkWebReqObjectArgAutoConfiguration implements WebMvcConfigurer {

        private final ObjectMapper objectMapper;
        private final OnelinkProperties onelinkProperties;

        @Bean
        public ObjectMethodArgumentResolver objectMethodArgumentResolver() {
            OnelinkProperties.Web.Arg requestArgProp = this.onelinkProperties.getWeb().getRequestArg();
            return new ObjectMethodArgumentResolver(this.objectMapper, requestArgProp);
        }

        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            log.info("[OneLinkFrm] 注册实体参数解析器[ObjectMethodArgumentResolver] [@RequestArg]");
            resolvers.add(this.objectMethodArgumentResolver());
        }

    }

    @Configuration(proxyBeanMethods = true)
    @ConditionalOnProperty(name = "onelink.web.request-arg.enabled", matchIfMissing = false)
    @ConditionalOnClass(IPage.class)
    @AllArgsConstructor
    @Slf4j
    static class OnelinkWebReqPageArgAutoConfiguration implements WebMvcConfigurer {

        private final ObjectMapper objectMapper;
        private final OnelinkProperties onelinkProperties;

        @Bean
        public PageMethodArgumentResolver pageMethodArgumentResolver() {
            return new PageMethodArgumentResolver(this.objectMapper, this.onelinkProperties.getWeb().getRequestArg());
        }

        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            OnelinkProperties.Web.Arg requestArg = this.onelinkProperties.getWeb().getRequestArg();
            log.info("[OneLinkFrm] 注册分页参数解析器[PageMethodArgumentResolver]");
            // 优先级需要比ObjectMethodArgumentResolver高
            resolvers.add(0, this.pageMethodArgumentResolver());
        }

    }

    /**
     * 设置jackson注解扫描器
     */
    private void setJacksonObjectMapperAnnotationIntrospector(ObjectMapper objectMapper, OnelinkProperties onelinkProperties) {
        Collection<AnnotationIntrospector> annotationIntrospectors = objectMapper.getSerializationConfig().getAnnotationIntrospector().allIntrospectors();
        if (CollUtil.isEmpty(annotationIntrospectors)) {
            return;
        }
        AnnotationIntrospector primary = CollUtil.findOne(annotationIntrospectors, annotationIntrospector -> annotationIntrospector instanceof ParameterNamesAnnotationIntrospector);
        if (primary == null) {
            primary = annotationIntrospectors.iterator().next();
        }
        OnelinkJacksonAnnotationIntrospector second = new OnelinkJacksonAnnotationIntrospector(onelinkProperties);
        AnnotationIntrospectorPair annotationIntrospectorPair = new AnnotationIntrospectorPair(primary, second);
        objectMapper.setAnnotationIntrospector(annotationIntrospectorPair);
    }


    /**
     * 设置jackson注解扫描器
     */
    private void setModule(ObjectMapper objectMapper, OnelinkProperties onelinkProperties) {
        SimpleModule module = new SimpleModule("ONELINK_JACKSON_MODULE");
        module.setDeserializers(new JacksonEnumSimpleDeserializers());
        module.addDeserializer(Date.class, new JacksonDateDeserializer());
        objectMapper.registerModule(module);
    }

}
